--- title: "03-Java 构造方法详解" aliases: - "Java 构造方法详解" created: 2025-12-25 --- # Java 构造方法详解 --- ## 一、构造方法概述 ### **1.1 什么是构造方法** ```drawio ``` ### **1.2 基本语法** ``` [访问修饰符] 类名(参数列表) { // 初始化代码 } ``` ```java public class Student { private String name; private int age; // 构造方法 public Student(String name, int age) { this.name = name; this.age = age; } } // 使用 Student stu = new Student("张三", 20); ``` ### **1.3 构造方法 vs 普通方法** | **对比项** | **构造方法** | **普通方法** | | --- | --- | --- | | **命名** | 必须与类名相同 | 任意合法标识符 | | **返回值** | 无(连 void 都没有) | 必须声明返回类型 | | **调用时机** | new 时自动调用 | 手动调用 | | **调用次数** | 每个对象只调用一次 | 可多次调用 | | **作用** | 初始化对象 | 执行功能 | | **继承** | 不能被继承 | 可以被继承 | | **重写** | 不能重写 | 可以重写 | --- ## 二、默认构造方法 ### **2.1 编译器自动生成** ```drawio ``` #### **代码示例** ```java // 源代码(没有写构造方法) public class Product { private String name; private double price; } // 编译后等价于(编译器自动添加) public class Product { private String name; private double price; // 编译器添加的默认构造方法 public Product() { // 空实现 } } ``` ``` // 可以正常使用无参构造 Product p = new Product(); // ✅ 正确 ``` ### **2.2 查看字节码验证** ``` # 编译 javac Product.java # 查看字节码 javap -c Product.class ``` **输出结果**: ```java public class Product { public Product(); // ← 编译器生成的默认构造方法 Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."":()V 4: return } ``` ### **2.3 默认构造方法的特点** | **特点** | **说明** | | --- | --- | | 访问修饰符 | 与类的访问修饰符相同 | | 参数 | 无参数 | | 方法体 | 空(仅调用父类无参构造) | | 生成条件 | 类中没有任何显式构造方法 | ```java // 类是 public,默认构造也是 public public class A { } // 类是默认访问权限,默认构造也是默认权限 class B { } ``` --- ## 三、手动定义构造方法 ### **3.1 ⚠️ 重要规则** ```drawio ``` ### **3.2 错误示例** ```java public class Product { private String name; // 手动定义了有参构造 public Product(String name) { this.name = name; } } // 使用 public class Main { public static void main(String[] args) { Product p1 = new Product("手机"); // ✅ 正确 Product p2 = new Product(); // ❌ 编译错误! } } ``` **编译错误信息**: ```yaml Error: java: 无法将类 Product 中的构造器 Product 应用到给定类型 需要: java.lang.String 找到: 没有参数 原因: 实际参数列表和形式参数列表长度不同 ``` ### **3.3 解决方案:显式定义无参构造** ```java public class Product { private String name; // ✅ 显式定义无参构造 public Product() { this.name = "未命名商品"; } // 有参构造 public Product(String name) { this.name = name; } } // 现在都可以使用 Product p1 = new Product(); // ✅ 调用无参构造 Product p2 = new Product("手机"); // ✅ 调用有参构造 ``` ### **3.4 流程图:默认构造的生成逻辑** ```drawio ``` --- ## 四、构造方法重载 ### **4.1 什么是构造方法重载** ```drawio ``` ### **4.2 代码示例** ```java public class Student { private String name; private int age; private String school; // 构造方法 1:无参 public Student() { this.name = "未知"; this.age = 0; this.school = "未知"; } // 构造方法 2:一个参数 public Student(String name) { this.name = name; this.age = 0; this.school = "未知"; } // 构造方法 3:两个参数 public Student(String name, int age) { this.name = name; this.age = age; this.school = "未知"; } // 构造方法 4:三个参数(全参构造) public Student(String name, int age, String school) { this.name = name; this.age = age; this.school = school; } } ``` ``` // 多种创建方式 Student s1 = new Student(); // 调用构造1 Student s2 = new Student("张三"); // 调用构造2 Student s3 = new Student("李四", 20); // 调用构造3 Student s4 = new Student("王五", 21, "清华大学"); // 调用构造4 ``` ### **4.3 使用 this() 调用其他构造方法** ```java public class Student { private String name; private int age; private String school; // 全参构造(主构造方法) public Student(String name, int age, String school) { this.name = name; this.age = age; this.school = school; System.out.println("创建学生:" + name); } // 无参构造 → 调用全参构造 public Student() { this("未知", 0, "未知"); // ✅ 调用全参构造 } // 一参构造 → 调用全参构造 public Student(String name) { this(name, 0, "未知"); // ✅ 调用全参构造 } // 两参构造 → 调用全参构造 public Student(String name, int age) { this(name, age, "未知"); // ✅ 调用全参构造 } } ``` **调用链示**意图: ```drawio ``` ### **4.4 this() 使用规则** | **规则** | **说明** | | --- | --- | | 必须在第一行 | `this()` 必须是构造方法的第一条语句 | | 只能调用一次 | 一个构造方法中只能有一个 `this()` | | 不能循环调用 | A 调 B,B 不能再调 A | | 不能和 super() 共存 | 二者都要求在第一行 | ```java // ❌ 错误示例 public Student() { System.out.println("准备创建"); // 错误!this() 必须在第一行 this("默认"); } // ❌ 错误示例:循环调用 public Student() { this("默认"); // 调用一参构造 } public Student(String name) { this(); // 又调用无参构造 → 死循环!编译器报错 } ``` --- ## 五、构造方法的特点总结 ### **5.1 五大特点一览表** | **序号** | **特点** | **说明** | **示例** | | --- | --- | --- | --- | | 1️⃣ | 方法名与类名相同 | 严格一致,包括大小写 | `class Dog { Dog() {} }` | | 2️⃣ | 没有返回值类型 | 连 void 都不写 | `public Student() {}` | | 3️⃣ | 自动调用 | new 时自动执行 | `new Student()` | | 4️⃣ | 可以重载 | 多个构造,参数不同 | 见上文示例 | | 5️⃣ | 不能被修饰 | 不能用 static/final/abstract | — | --- ### **5.2 特点详解图** ```drawio ``` ### **5.3 易混淆点:构造方法 vs 同名普通方法** ```java public class Demo { // ✅ 这是构造方法(无返回类型) public Demo() { System.out.println("构造方法执行"); } // ⚠️ 这是普通方法!(有返回类型 void) public void Demo() { System.out.println("普通方法执行"); } } public class Main { public static void main(String[] args) { Demo d = new Demo(); // 输出:构造方法执行 d.Demo(); // 输出:普通方法执行 } } ``` > *⚠️ **注意**:虽然语法允许定义与类名相同的普通方法,但这是极差的编码习惯!* --- ## 六、构造方法与继承 ### **6.1 子类构造调用父类构造** ```drawio ``` ### **6.2 代码示例** ```java class Animal { protected String name; public Animal() { System.out.println("Animal 无参构造"); } public Animal(String name) { this.name = name; System.out.println("Animal 有参构造:" + name); } } class Dog extends Animal { private String breed; // 隐式调用 super() public Dog() { // 编译器自动添加:super(); System.out.println("Dog 无参构造"); } // 显式调用 super(name) public Dog(String name, String breed) { super(name); // ✅ 必须在第一行 this.breed = breed; System.out.println("Dog 有参构造:" + breed); } } ``` ``` // 测试 Dog d1 = new Dog(); // 输出: // Animal 无参构造 // Dog 无参构造 Dog d2 = new Dog("旺财", "金毛"); // 输出: // Animal 有参构造:旺财 // Dog 有参构造:金毛 ``` ### **6.3 构造方法调用顺序图** ```drawio ``` ### **6.4 常见错误:父类没有无参构造** ```java class Parent { public Parent(String name) { // 只有有参构造 System.out.println("Parent: " + name); } } class Child extends Parent { public Child() { // 编译器隐式添加 super(),但 Parent 没有无参构造! // ❌ 编译错误! } } ``` **解决**方案: ```java // 方案 1:子类显式调用父类有参构造 class Child extends Parent { public Child() { super("默认名称"); // ✅ 显式调用 } } // 方案 2:父类添加无参构造 class Parent { public Parent() { } // ✅ 添加无参构造 public Parent(String name) { System.out.println("Parent: " + name); } } ``` --- ## 七、最佳实践 ### **7.1 实践规则一览表** | **序号** | **规则** | **原因** | | --- | --- | --- | | 1 | 始终保留无参构造 | 框架反射、序列化需要 | | 2 | 使用 this() 避免重复 | 代码复用,统一初始化 | | 3 | 参数校验放构造中 | 确保对象状态合法 | | 4 | 避免调用可重写方法 | 防止子类未初始化完成 | | 5 | 考虑使用建造者模式 | 参数过多时更清晰 | --- ### **7.2 实践 1:始终保留无参构造** ```java public class User { private String username; private String email; // ✅ 始终保留无参构造 public User() { } public User(String username, String email) { this.username = username; this.email = email; } } ``` **需要无参构造的场**景: ```drawio ``` ### **7.3 实践 2:使用 this() 避免代码重复** ```java // ❌ 不好的写法:重复代码 public class Product { private String name; private double price; private String category; public Product() { this.name = "未命名"; this.price = 0.0; this.category = "未分类"; validate(); // 重复 log(); // 重复 } public Product(String name) { this.name = name; this.price = 0.0; this.category = "未分类"; validate(); // 重复 log(); // 重复 } public Product(String name, double price, String category) { this.name = name; this.price = price; this.category = category; validate(); // 重复 log(); // 重复 } } ``` ```java // ✅ 好的写法:使用 this() 委托 public class Product { private String name; private double price; private String category; // 全参构造(核心构造方法) public Product(String name, double price, String category) { this.name = name; this.price = price; this.category = category; validate(); // 只写一次 log(); // 只写一次 } // 委托给全参构造 public Product() { this("未命名", 0.0, "未分类"); } public Product(String name) { this(name, 0.0, "未分类"); } public Product(String name, double price) { this(name, price, "未分类"); } } ``` ### **7.4 实践 3:在构造方法中进行参数校验** ```java public class BankAccount { private String accountNumber; private double balance; public BankAccount(String accountNumber, double initialBalance) { // ✅ 参数校验 if (accountNumber == null || accountNumber.trim().isEmpty()) { throw new IllegalArgumentException("账号不能为空"); } if (initialBalance < 0) { throw new IllegalArgumentException("初始余额不能为负"); } this.accountNumber = accountNumber; this.balance = initialBalance; } } ``` ``` // 使用 BankAccount acc1 = new BankAccount("123456", 1000); // ✅ 正常 BankAccount acc2 = new BankAccount("", 100); // ❌ 抛出异常 BankAccount acc3 = new BankAccount("123", -100); // ❌ 抛出异常 ``` ### **7.5 实践 4:避免在构造方法中调用可重写方法** ```java // ❌ 危险的写法 class Parent { public Parent() { init(); // 调用可重写方法 } public void init() { System.out.println("Parent init"); } } class Child extends Parent { private String name; public Child(String name) { super(); // 调用父类构造 this.name = name; } @Override public void init() { // ⚠️ 此时 name 还是 null!因为子类构造还没执行完 System.out.println("Child init: " + name.toUpperCase()); // NullPointerException! } } ``` **问题分析**图: ```drawio ``` **解决方**案: ```java // ✅ 使用 final 方法或 private 方法 class Parent { public Parent() { initInternal(); // 调用 private 方法 } private void initInternal() { // 不能被重写 System.out.println("Parent init"); } } ``` ### **7.6 实践 5:参数过多时使用建造者模式** ``` // ❌ 构造参数过多,难以阅读 User user = new User("张三", 20, "男", "北京", "100000", "13800138000", "zhangsan@email.com", true); // ✅ 使用建造者模式 User user = User.builder() .name("张三") .age(20) .gender("男") .city("北京") .build(); ``` **建造者模式实**现: ```java public class User { private final String name; private final int age; private final String gender; private final String city; // 私有构造,只能通过 Builder 创建 private User(Builder builder) { this.name = builder.name; this.age = builder.age; this.gender = builder.gender; this.city = builder.city; } // 静态方法获取 Builder public static Builder builder() { return new Builder(); } // 静态内部类 Builder public static class Builder { private String name; private int age; private String gender; private String city; public Builder name(String name) { this.name = name; return this; } public Builder age(int age) { this.age = age; return this; } public Builder gender(String gender) { this.gender = gender; return this; } public Builder city(String city) { this.city = city; return this; } public User build() { return new User(this); } } } ``` --- ## 八、核心总结 ### **8.1 知识要点速查表** | **主题** | **要点** | | --- | --- | | **默认构造** | 没有任何构造方法时编译器自动生成 | | **消失条件** | 定义任何构造方法后默认构造消失 | | **重载** | 同类中多个构造,参数不同 | | **this()** | 调用本类其他构造,必须在第一行 | | **super()** | 调用父类构造,必须在第一行 | | **执行顺序** | Object → 父类 → 子类 | --- ### **8.2 构造方法完整生命周期图** ```drawio ``` ### **8.3 最佳实践清单** ```drawio ```